package models;

import java.util.*;

import javax.persistence.*;

import controllers.Browse;

import play.db.jpa.*;
import play.modules.search.Field;
import play.modules.search.Indexed;

/**
 * 
 * @author Mostafa Fahim
 * @version 1.0
 */
@Entity
@Indexed
public class EEntity extends Model {

	@Field
	public String name;
	@Lob
	@Field
	public String description;
	@ManyToOne
	public Organization organization;
	@OneToMany(mappedBy = "entity", cascade = CascadeType.PERSIST)
	public List<Topic> topics;
	@ManyToMany
	public List<ActionItem> items;
	@ManyToMany(mappedBy = "followedEnts")
	public List<User> followingUsers;
	@OneToMany(mappedBy = "entity", cascade = CascadeType.PERSIST)
	public List<RoleInEntity> rolesAndUsers;
	public boolean isLock;
	@OneToMany(mappedBy = "firstEntity", cascade = CascadeType.PERSIST)
	public List<RelatedEntities> relations;
	@OneToMany(mappedBy = "secondEntity", cascade = CascadeType.PERSIST)
	public List<RelatedEntities> relatesTo;
	public int numberOfView;
	@OneToMany(mappedBy = "entity", cascade = CascadeType.PERSIST)
	public List<Request> entityRequests;
	@ManyToMany
	public List<User> blockedFromEntity;

	/**
	 * 
	 * @param name
	 *            the name of the entity
	 * @param description
	 *            the description of the aim of this entity(what it does)
	 * @param organization
	 *            the organization that this entity belongs to
	 */

	public EEntity(String name, String description, Organization organization,
			boolean lock) {
		this.name = name;
		this.description = description;
		this.organization = organization;
		this.topics = new ArrayList<Topic>();
		this.items = new ArrayList<ActionItem>();
		rolesAndUsers = new ArrayList<RoleInEntity>();
		this.isLock = lock;
		System.out.println("henaaa");
		this.relations = new ArrayList<RelatedEntities>();
		this.relatesTo = new ArrayList<RelatedEntities>();
		System.out.println("henaaa1111");
		this.followingUsers = new ArrayList<User>();
		this.numberOfView = 0;
		blockedFromEntity = new ArrayList<User>();

	}

	/**
	 * @author Zeyad Gasser
	 * @component 1
	 * @userStory 14 returns an entity after giving it an id
	 * @param id
	 * @return
	 */
	public static EEntity getEntityById(long id) {
		return EEntity.find("byId", id).first();
	}

	/**
	 * method to add new action item to this entity
	 * 
	 * @param itemId
	 *            id of the item to be added to this entity
	 * 
	 * @return the method return the EEntity instance after the modifications
	 */

	public EEntity addActionItem(long itemId) {
		ActionItem item = ActionItem.findById(itemId);
		item.setEntity(this.getId());
		this.items.add(item);
		this.save();
		LinkedList<User> toBeNotified = new LinkedList<User>();
		for (User user : this.followingUsers) {
			if (!toBeNotified.contains(user))
				toBeNotified.add(user);
		}
		for (User user : this.organization.followingUsers) {
			if (!toBeNotified.contains(user))
				toBeNotified.add(user);
		}
		Notification newNotification = new Notification(
				"A new action item has been added" + " to " + this.name,
				item.id, 0, item.plan.creator, toBeNotified);
		/*
		 * for(User user : toBeNotified){
		 * user.notifications.add(newNotification); user.save(); }
		 */
		return this;
	}

	/**
	 * 
	 * @param name
	 *            the name of the entity
	 * @param description
	 *            a description of the aim of this entity
	 * @param organization
	 *            the organization to which this entity belong
	 */
	public EEntity(String name, String description, Organization organization) {
		this.name = name;
		this.description = description;
		this.organization = organization;
		this.topics = new ArrayList<Topic>();
		rolesAndUsers = new ArrayList<RoleInEntity>();
		this.followingUsers = new ArrayList<User>();
		this.relations = new ArrayList<RelatedEntities>();
		blockedFromEntity = new ArrayList<User>();
	}

	public EEntity addRoleAndUser(RoleInEntity roleAndUser) {
		this.rolesAndUsers.add(roleAndUser);
		this.save();
		return this;
	}

	public List<Topic> getTopics() {
		return topics;
	}

	/**
	 * This method returns all entities whose name matches the keyword or if the 
	 * description contains the keyword in it
	 * @author Omar Fouda
	 * @param keyword
	 *            , the keyword to be used for the search. 
	 * @return a list of entities matching the keyword
	 */

	public static List<EEntity> searchByKeyword(String keyword) {

		List<EEntity> allEntities = EEntity.findAll();

		ArrayList<String> allTitles = new ArrayList<String>(allEntities.size());
		ArrayList<String> allDescriptions = new ArrayList<String>(
				allEntities.size());

		List<EEntity> entitiesMatchingKeyword = new ArrayList<EEntity>();

		for (int i = 0; i < allEntities.size(); i++) {
			allTitles.add(allEntities.get(i).name.toLowerCase());

			allDescriptions
					.add(i, allEntities.get(i).description.toLowerCase());
		}

		for (int i = 0; i < allEntities.size(); i++) {
			if (allTitles.get(i).equalsIgnoreCase(keyword)
					|| allDescriptions.get(i).contains(keyword.toLowerCase())) {
				entitiesMatchingKeyword.add(allEntities.get(i));
			}
		}

		return entitiesMatchingKeyword;

	}

	/**
	 * A method to send a topic request.
	 * 
	 * @param name
	 *            the name of the topic requested.
	 * @param description
	 *            the description of the topic requested.
	 * @param creator
	 *            the user who saved the request as a draft.
	 * @return the id of the topic requested.
	 */

	public long postRequest(String name, String description, User creator) {
		Topic topic = new Topic(name, 3, description, new Date(), this, creator)
				.save();
		creator.createdTopics.add(topic);
		this.topics.add(topic);
		creator.save();
		this.save();
		topic.save();
		return topic.id;
	}

	/**
	 * A method to save a topic request as a draft.
	 * 
	 * @author Hesham Massoud
	 * 
	 * @param name
	 *            the name of the topic requested.
	 * @param description
	 *            the description of the topic requested.
	 * @param creator
	 *            the user who saved the request as a draft.
	 * @return the id of the topic requested saved as draft.
	 */
	public long saveRequestDraft(String name, String description, User creator) {
		Topic topic = new Topic(name, 3, description, new Date(), this,
				creator, true).save();
		creator.createdTopics.add(topic);
		this.topics.add(topic);
		creator.save();
		this.save();
		return topic.id;
	}

	/**
	 * A method returning a list of organizers
	 * 
	 * @return A list of the entity's organizers
	 * 
	 * @author Johannes Schmidt
	 * 
	 * */
	public List<User> getOrganizers() {
		ArrayList<User> result = new ArrayList<User>();
		for (RoleInEntity roleAndUser : rolesAndUsers) {
			if (roleAndUser.role.description.equalsIgnoreCase("Organizer"))
				result.add(roleAndUser.user);
		}
		return result;
	}

	/**
	 * A method to add a topic to an entity
	 * 
	 * @param name
	 *            the name of the topic to be added.
	 * @param status
	 *            the status of the topic to be added, 0->active, 1->inactive,
	 *            2->closed, 3->request.
	 * @param description
	 *            the description of the topic to be created
	 * @param date
	 *            date of creation
	 * @param entity
	 *            the entity in which the topic will be added
	 * @param creator
	 *            the creator of the topic
	 * @return the entity where the topic is posted
	 */

	public EEntity addTopic(String name, int status, String description,

	EEntity entity, User creator) {

		Topic topic = new Topic(name, status, description, new Date(), entity,
				creator).save();

		creator.createdTopics.add(topic);
		creator.save();

		this.topics.add(topic);
		this.save();
		LinkedList<User> toBeNotified = new LinkedList<User>();
		for (User user : this.followingUsers) {
			if (!toBeNotified.contains(user))
				toBeNotified.add(user);
		}
		for (User user : this.organization.followingUsers) {
			if (!toBeNotified.contains(user))
				toBeNotified.add(user);
		}
		Notification newNotification = new Notification(
				"A new topic has been added to Entity " + this.name, topic.id,
				11, creator, toBeNotified);
		newNotification.timeStamp = new Date();
		newNotification.save();

		if (creator.isOrganizer(entity.id)) {

			creator.followedTopics.add(topic);
			topic.followingUsers.add(creator);
			creator.save();
			topic.save();
		}

		return this;
	}

	public void lockEntity() {// This Method is done to lock an Entity so that
								// users can't
								// relate to it
		isLock = true;
	}

	public void unlockEntity() {// This Method is done to unlock an Entity so
								// that users can
		// relate to it
		isLock = false;
	}

	public boolean checkLock() {
		return isLock;

	}

	/**
	 * This method returns a list of the related entities of this specific
	 * entity
	 * 
	 * @return list of these entities
	 * */
	public List<EEntity> getRelatedEntities() {
		List<EEntity> relatedEntities = new ArrayList<EEntity>();
		for (int i = 0; i < this.relations.size(); i++) {
			relatedEntities.add(this.relations.get(i).secondEntity);
		}
		return relatedEntities;
	}

	public void addTopic(String string, int i, String string2, Date date,
			EEntity en, User user) {
		// TODO Auto-generated method stub

	}

	/**
	 * @author AkrAmAtion
	 * 
	 * @return a String that will be used to export search results displaying an
	 *         entity whose name matches a keyword. The String is formatted as
	 *         Comma-Separated-Values
	 * */
	public String toString() {
		return "ENTITY," + name + "," + description + "," + organization;
	}

	/**
	 * Method to check if a user is a member of a certain entity by calling
	 * findRoles and checking whether or not he has a role
	 * 
	 * @author Omar Fouda
	 * @param entityID
	 *            , id of the entity we want to check membership in
	 * @param userID
	 *            , id of the user whose membership we want to check
	 * @return result, true if he is a member of this id, false otherwise
	 **/

	public boolean isMember(long entityID, long userID) {
		if (userID == -1)
			return false;
		// Get the user object
		User user = User.find("byID", userID).first();
		EEntity entity = EEntity.findById(entityID);
		// If the role of the user is not null, he is member of the organization
		boolean result = RoleInEntity.findRole(entity, user) != null;
		return result;
	}

	/**
	 * @author Yomna El Rashidi
	 * 
	 * gets the lock value of the entity
	 * @return boolean value true if the entity is locked and false if the
	 *         entity is not locked
	 * 
	 */
	public boolean getLock() {
		return this.isLock;
	}

	/**
	 * @author Yomna El Rashidi
	 * 
	 *         gets the name of the relation between two entities
	 * @param entity1
	 *            the first entity within the relation
	 * @param entity2
	 *            the second entity within the relation
	 * @return returns a string containing the relation Name between the
	 *         entities.
	 */

	public String getRelationName(EEntity entity1, EEntity entity2) {
		List<RelatedEntities> relations = entity1.relations;
		List<RelatedEntities> relatedTo = entity1.relatesTo;
		boolean found = false;
		String relationName = "";
		for (RelatedEntities relation : relations) {
			if (relation.firstEntity.equals(entity1)
					&& relation.secondEntity.equals(entity2)) {
				found = true;
				relationName = relation.relationName;
				break;
			}

		}
		if (!found) {
			for (RelatedEntities relatedToIt : relatedTo) {

				if (relatedToIt.firstEntity.equals(entity2)
						&& relatedToIt.secondEntity.equals(entity1)) {
					found = true;
					relationName = relatedToIt.relationName;
					break;
				}
			}
		}
		return relationName;
	}

	/**
	 * This method removes an entity member from the entity, it removes him from
	 * the roles in the entity and then deletes the record from the RoleInEntity
	 * table, deletes him from the list of users following the entity and
	 * deletes the entity from the list of entities being followed by him
	 * removes him from the ideas and topics he's following and removes them
	 * from his object instance
	 * 
	 * @author Omar Fouda
	 * @param userId
	 *            , id of the user to be removed
	 */

	public void removeMember(long userId) {
		User userToBeRemoved = User.findById(userId);

		RoleInEntity roleToBeRemoved = null;

		for (int i = 0; i < this.rolesAndUsers.size(); i++) {
			if (this.rolesAndUsers.get(i).user == userToBeRemoved) {
				roleToBeRemoved = this.rolesAndUsers.get(i);
				this.rolesAndUsers.remove(i);
			}
		}
		System.out
				.println(models.RoleInEntity.delete("ID", roleToBeRemoved.id));

		for (int i = 0; i < this.followingUsers.size(); i++) {
			if (this.followingUsers.get(i).userName
					.equals(userToBeRemoved.userName)) {
				this.followingUsers.remove(i);
			}
		}

		for (int i = 0; i < userToBeRemoved.followedEnts.size(); i++) {
			if (userToBeRemoved.followedEnts.get(i).equals(this)) {
				userToBeRemoved.followedEnts.remove(i);
			}
		}

		for (int i = 0; i < userToBeRemoved.followedTopics.size(); i++) {
			if (userToBeRemoved.followedTopics.get(i).entity.equals(this)) {
				userToBeRemoved.followedTopics.remove(i);
			}
		}

		for (int i = 0; i < this.topics.size(); i++) {
			if (this.topics.get(i).followingUsers.contains(userToBeRemoved)) {
				this.topics.get(i).followingUsers.remove(userToBeRemoved);

			}
		}

		/*
		 * for (int i = 0; i < userToBeRemoved.subscribedIdeas.size(); i++) { if
		 * (userToBeRemoved.subscribedIdeas.get(i).topic.entity .equals(this)) {
		 * userToBeRemoved.subscribedIdeas.get(i).subscribers
		 * .remove(userToBeRemoved);
		 * userToBeRemoved.subscribedIdeas.get(i).save(); } }
		 */

		this.save();
		userToBeRemoved.save();
	}

	/**
	 * Method to check if a user is an idea developer of a certain entity
	 * 
	 * @author Shady
	 * @param entityID
	 *            , id of the entity we want to check membership in
	 * @param userID
	 *            , id of the user whose membership we want to check
	 * @return result, true if he is a member of this id, false otherwise
	 **/
	public static boolean isIdeaDeveloper(long entityID, long userID) {
		// Get the user object
		User user = User.find("byID", userID).first();
		EEntity entity = EEntity.findById(entityID);

		if (user == null || entity == null) {
			return false;
		}

		// If the role of the user is not null, he is member of the organization
		Role role = RoleInEntity.findRole(entity, user);
		if (role == null) {
			return false;
		}
		boolean result = role.description.equalsIgnoreCase("IdeaDeveloper");
		return result;
	}
	/**
	 * A method that gets a summarized string of the description of the entity
	 * to be used in certain views to give a brief overview about the entity
	 * 
	 * @author Mostafa Ali
	 * 
	 * @return string containing brief description(maximum 40 characters)
	 * 
	 * */
	
	public String getSummarizedDescription() {
		if (description.length() < 40)
			return description;
		else
			return description.substring(0, 40) + "...";
	}

	/**
	 * @author Ahmed Mahmoud
	 * @return
	 */
	public boolean cascadeDelete() {
		organization.entities.remove(this);
		organization.save();
		while (!topics.isEmpty()) {
			Topic topic = topics.get(0);
			topic.cascadeDelete();

		}
		while (!items.isEmpty()) {
			ActionItem actionItem = items.get(0);
			actionItem.cascadeDelete();
		}
		for (int i = 0; i < followingUsers.size(); i++) {
			User user = followingUsers.get(i);
			user.followedEnts.remove(this);
			user.save();
		}
		while (!rolesAndUsers.isEmpty()) {
			RoleInEntity roleInEntity = rolesAndUsers.get(0);
			roleInEntity.cascadeDelete();
		}
		while (!relatesTo.isEmpty()) {
			RelatedEntities relatedEntities = relatesTo.get(0);
			relatedEntities.cascadeDelete();
		}
		while (!relations.isEmpty()) {
			RelatedEntities relatedEntities = relations.get(0);
			relatedEntities.cascadeDelete();
		}
		while (!entityRequests.isEmpty()) {
			Request request = entityRequests.get(0);
			request.cascadeDelete();
		}
		for (int i = 0; i < blockedFromEntity.size(); i++) {
			User user = blockedFromEntity.get(i);
			user.blockedEntities.remove(this);
			user.save();
		}
		this.delete();
		return true;
	}

}
